﻿//========================================================= 
/**@file H002PinPadExDevAdapter.h
 * @brief H002密码键盘非堆成加密算法扩展指令
 * 
 * @date 2017-12-12   16:42:57
 * @author Zhyioo 
 * @version 1.0
 */ 
//--------------------------------------------------------- 
#ifndef _LIBZHOUYB_H002PINPADEXDEVADAPTER_H_
#define _LIBZHOUYB_H002PINPADEXDEVADAPTER_H_
//--------------------------------------------------------- 
#include "H002PinPadDevAdapter.h"
//--------------------------------------------------------- 
namespace zhou_yb {
namespace application {
namespace pinpad {
//--------------------------------------------------------- 
/**
 * @brief H002密码键盘国密部分指令集
 * @date 2017-12-08 11:00
 */
class H002PinPadExDevAdapter : public DevAdapterBehavior<IInteractiveTrans>, public RefObject
{
protected:
    //----------------------------------------------------- 
    /// 发送数据的临时缓冲区 
    ByteBuilder _sendBuffer;
    /// 接收数据的临时缓冲区 
    ByteBuilder _recvBuffer;
    //----------------------------------------------------- 
public:
    //----------------------------------------------------- 
    /**
     * 产生非对称算法密钥对并传出公钥
     * @param kIndex 非对称密钥密钥区号01-04
     * @param kType  算法类型
     * - 类型:
     *  - 01H: 国际算法
     *  - 02H: 国密算法
     * .
     * @param kLength  密钥长度：
     * 算法类型为02H，此参数忽略
     * 国际RSA算法。最长支持密钥长度2048，其中2048长度密钥用0x01表示；
     * 其余长度密钥用模长转换为16进制表示，例如 1152长度密钥，模长为144，转换为16进制格式为0x90。
     *
     * @param key 公钥
     */
    bool GenPublicKEY(byte kIndex, byte kType,ByteBuilder& key,byte kLength)
    {
        LOG_FUNC_NAME();
        LOGGER(_log << "非对称密钥密钥:<" << static_cast<int>(kIndex) << ">\n";
        _log << "算法类型:<" << static_cast<int>(kType) << ">\n";
        _log << "密钥长度:<" << static_cast<int>(kLength) << ">\n");
        ASSERT_Device();
        _sendBuffer.Clear();
        _recvBuffer.Clear();
        DevCommand::FromAscii("30 02", _sendBuffer);
        _sendBuffer += kIndex;
        _sendBuffer += kType;
        ByteConvert::FromObject<uint>(kLength, _sendBuffer, 2);
        ASSERT_FuncErrRet(_pDev->Write(_sendBuffer), DeviceError::SendErr);
        ASSERT_FuncErrRet(_pDev->Read(_recvBuffer), DeviceError::RecvErr);
        LOGGER(_log.WriteLine("PublicKEY:").WriteLine(_recvBuffer));
        key.Append(_recvBuffer);
        return _logRetValue(true);
    }
    /**
     * @brief 下载非对称密钥
     * @date 2017-12-08 09:51
     * 
     * @param [in] kIndex 需要下载的非对称密钥序号 [1,4]
     * @param [in] kType 密钥类型
     * - 密钥类型:
     *  - 1 : RSA公钥
     *  - 2 : RSA私钥
     *  - 3 : SM2公钥
     *  - 4 : SM2私钥
     * .
     * @param [in] key 需要下载的密钥
     */
    bool DownloadKey(byte kIndex, byte kType, const ByteArray& key)
    {
        LOG_FUNC_NAME();
        LOGGER(_log << "主密钥:<" << static_cast<int>(kIndex) << ">\n";
        _log << "密钥类型:<" << static_cast<int>(kType) << ">\n";
        _log.WriteLine("密钥:").WriteLine(key));
        ASSERT_Device();
        _sendBuffer.Clear();
        _recvBuffer.Clear();
        DevCommand::FromAscii("30 81", _sendBuffer);
        _sendBuffer += kIndex;
        _sendBuffer += kType;
        size_t len = key.GetLength();
        // RSA 2048位密钥
        if(len == 256)
            len = 0x01;
        ByteConvert::FromObject<size_t>(len, _sendBuffer, 2);
        _sendBuffer += key;
        ASSERT_FuncErrRet(_pDev->Write(_sendBuffer), DeviceError::SendErr);
        ASSERT_FuncErrRet(_pDev->Read(_recvBuffer), DeviceError::RecvErr);
        return _logRetValue(true);
    }
    /**
     * @brief 公钥产生会话密钥
     * @date 2017-12-11 12:48
     * 
     * @param [in] alg 算法用途
     * - 算法
     *  - 01 - DES
     *  - 02 - 2DES
     *  - 03 - SM4
     *  - 04 - 3DES
     * .
     * @param [in] isSM2 是否为SM2公钥,否则为RSA公钥
     * @param [in] kIndex 非对称密钥索引号 [1,4]
     * @param [in] klen 需要生成的密钥长度(字节)
     *
     * @param [out] key 生成的公钥数据
     * @param [out] kcv 公钥数据的KCV
     */
    bool PublicGenKey(byte alg, bool isSM2, byte kIndex, size_t klen, ByteBuilder& key, ByteBuilder& kcv)
    {
        LOG_FUNC_NAME();
        LOGGER(_log << "算法标识:<" << static_cast<int>(alg) << ">\n";
        _log << "密钥类型:<" << (isSM2 ? "SM2" : "RSA") << ">\n";
        _log << "密钥区号 :<" << static_cast<int>(kIndex) << ">\n";
        _log << "密钥长度:<" << static_cast<int>(klen) << ">\n");
        ASSERT_Device();
        _sendBuffer.Clear();
        _recvBuffer.Clear();
        DevCommand::FromAscii("30 82", _sendBuffer);
        _sendBuffer += alg;
        _sendBuffer += (isSM2 ? 0x03 : 0x01);
        _sendBuffer += kIndex;
        // RSA密钥
        if(!isSM2)
        {
            if(klen == 256)
                klen = 0x01;
        }
        _sendBuffer += _itobyte(klen);
        ASSERT_FuncErrRet(_pDev->Write(_sendBuffer), DeviceError::SendErr);
        ASSERT_FuncErrRet(_pDev->Read(_recvBuffer), DeviceError::RecvErr);
        // Len x1 + KEY + Len x1 + KCV
        ASSERT_FuncErrRet(_recvBuffer.GetLength() >= 2, DeviceError::RecvFormatErr);
        size_t len = _recvBuffer[0];
        ASSERT_FuncErrRet(_recvBuffer.GetLength() >= (len + 2), DeviceError::RecvFormatErr);
        size_t kcvlen = _recvBuffer[len + 1];
        ASSERT_FuncErrRet(_recvBuffer.GetLength() >= (len + 2 + kcvlen), DeviceError::RecvFormatErr);
        ByteArray buff = _recvBuffer.SubArray(1, len);
        LOGGER(_log.WriteLine("会话密钥:").WriteLine(buff));
        key.Append(buff);
        buff = _recvBuffer.SubArray(2 + len, kcvlen);
        LOGGER(_log.WriteLine("会话密钥KCV:").WriteLine(buff));
        kcv.Append(buff);
        return _logRetValue(true);
    }
    /**
     * @brief 私钥产生会话密钥
     * @date 2017-12-11 12:53
     * 
     * @param [in] alg 算法用途
     * - 算法
     *  - 01 - DES
     *  - 02 - 2DES
     *  - 03 - SM4
     *  - 04 - 3DES
     * .
     * @param [in] isSM2 是否为SM2公钥,否则为RSA公钥
     * @param [in] kIndex 非对称密钥索引号 [1,4]
     * @param [in] key 公钥加密的会话密文
     *
     * @param [out] kcv 会话密钥KCV
     */
    bool PrivateGenKey(byte alg, bool isSM2, byte kIndex, const ByteArray& key, ByteBuilder& kcv)
    {
        LOG_FUNC_NAME();
        LOGGER(_log << "算法标识:<" << static_cast<int>(alg) << ">\n";
        _log << "密钥类型:<" << (isSM2 ? "SM2" : "RSA") << ">\n";
        _log << "密钥区号 :<" << static_cast<int>(kIndex) << ">\n";
        _log.WriteLine("密钥:").WriteLine(key));
        ASSERT_Device();
        _sendBuffer.Clear();
        _recvBuffer.Clear();
        DevCommand::FromAscii("", _sendBuffer);
        _sendBuffer += alg;
        _sendBuffer += (isSM2 ? 0x04 : 0x02);
        _sendBuffer += kIndex;
        size_t klen = key.GetLength();
        // RSA密钥
        if(!isSM2)
        {
            if(klen == 256)
                klen = 0x01;
        }
        _sendBuffer += _itobyte(klen);
        _sendBuffer += key;
        ASSERT_FuncErrRet(_pDev->Write(_sendBuffer), DeviceError::SendErr);
        ASSERT_FuncErrRet(_pDev->Read(_recvBuffer), DeviceError::RecvErr);
        LOGGER(_log.WriteLine("会话密钥KCV:").WriteLine(_recvBuffer));
        kcv.Append(_recvBuffer);
        return _logRetValue(true);
    }
    /**
     * @brief 下载主密钥
     * @date 2017-12-12 10:14
     * 
     * @param [in] mkIndex 需要下载的对称密钥区号 [0,0x0F]
     * @param [in] alg 算法标识
     * - 算法
     *  - 01 - DES
     *  - 02 - 2DES
     *  - 03 - SM4
     *  - 04 - 3DES
     * .
     * @param [in] kType 密钥类型
     * - 类型:
     *  - 1 : RSA公钥
     *  - 2 : RSA私钥
     *  - 3 : SM2公钥
     *  - 4 : SM2私钥
     *  - 5 : 会话密钥
     * .
     * @param [in] kIndex 非对称密钥索引号 [1,4]
     * @param [in] key 需要下载的主密钥密文
     * @param [in] kcv 密钥校验值
     *
     * @param [out] data 下载后设备返回的数据
     * - 数据格式:
     *  - 1,3时:主密钥密文长度x1 + 主密钥密文xN + 主密钥长度x1 + KCVx16
     *  - 2,4,5时:密钥长度x1 + KCVx16
     * .
     */
    bool DownloadMK(byte mkIndex, byte alg, byte kType, byte kIndex, const ByteArray& key, const ByteArray& kcv, ByteBuilder& data)
    {
        LOG_FUNC_NAME();
        LOGGER(_log << "主密钥索引:<" << static_cast<int>(mkIndex) << ">\n";
        _log << "算法标识:<" << static_cast<int>(alg) << ">\n";
        _log << "密钥类型:<" << static_cast<int>(kType) << ">\n";
        _log << "非对称密钥区:<" << static_cast<int>(kIndex) << ">\n";
        _log.WriteLine("密钥密文:").WriteLine(key);
        _log.WriteLine("密钥KCV:").WriteLine(kcv));
        ASSERT_Device();
        _sendBuffer.Clear();
        _recvBuffer.Clear();
        DevCommand::FromAscii("30 83", _sendBuffer);
        _sendBuffer += mkIndex;
        _sendBuffer += alg;
        _sendBuffer += kType;
        _sendBuffer += kIndex;
        size_t klen = key.GetLength();
        if(klen == 256)
            klen = 0x01;
        _sendBuffer += _itobyte(klen);
        _sendBuffer += key;
        _sendBuffer += _itobyte(kcv.GetLength());
        _sendBuffer += kcv;
        ASSERT_FuncErrRet(_pDev->Write(_sendBuffer), DeviceError::SendErr);
        ASSERT_FuncErrRet(_pDev->Read(_recvBuffer), DeviceError::RecvErr);
        data.Append(_recvBuffer);
        return _logRetValue(true);
    }
    /**
     * @brief 下载工作密钥
     * @date 2017-12-12 11:17
     * 
     * @param [in] mkIndex 需要下载的对称主密钥序号 [0,0x0F]
     * @param [in] wkIndex 需要下载的对称工作密钥序号 [0,0x0F]
     * @param [in] alg 算法标识
     * - 算法
     *  - 01 - DES
     *  - 02 - 2DES
     *  - 03 - SM4
     *  - 04 - 3DES
     * .
     * @param [in] kType 密钥类型
     * - 类型:
     *  - 1 : RSA公钥
     *  - 2 : RSA私钥
     *  - 3 : SM2公钥
     *  - 4 : SM2私钥
     *  - 5 : 会话密钥
     *  - 6 : 主密钥
     * .
     * @param [in] kIndex 非对称密钥索引号 [1,4]
     * @param [in] key 需要下载的工作密钥密文
     * @param [in] kcv 密钥校验值
     *
     * @param [out] data 下载后设备返回的数据
     * - 数据格式:
     *  - 1,3时:工作密钥密文长度x1 + 工作密钥密文xN + 工作密钥长度x1 + KCVx16
     *  - 2,4,5,6时:密钥长度x1 + KCVx16
     * .
     */
    bool DownloadWK(byte mkIndex, byte wkIndex, byte alg, byte kType, byte kIndex, const ByteArray& key, const ByteArray& kcv, ByteBuilder& data)
    {
        LOG_FUNC_NAME();
        LOGGER(_log << "主密钥索引:<" << static_cast<int>(mkIndex) << ">\n";
        _log << "工作密钥索引:<" << static_cast<int>(wkIndex) << ">\n";
        _log << "算法标识:<" << static_cast<int>(alg) << ">\n";
        _log << "密钥类型:<" << static_cast<int>(kType) << ">\n";
        _log << "非对称密钥区:<" << static_cast<int>(kIndex) << ">\n";
        _log.WriteLine("密钥密文:").WriteLine(key);
        _log.WriteLine("密钥KCV:").WriteLine(kcv));
        ASSERT_Device();
        _sendBuffer.Clear();
        _recvBuffer.Clear();
        DevCommand::FromAscii("30 84", _sendBuffer);
        _sendBuffer += mkIndex;
        _sendBuffer += wkIndex;
        _sendBuffer += alg;
        _sendBuffer += kType;
        _sendBuffer += kIndex;
        size_t klen = key.GetLength();
        if(klen == 256)
            klen = 0x01;
        _sendBuffer += _itobyte(klen);
        _sendBuffer += key;
        _sendBuffer += _itobyte(kcv.GetLength());
        _sendBuffer += kcv;
        ASSERT_FuncErrRet(_pDev->Write(_sendBuffer), DeviceError::SendErr);
        ASSERT_FuncErrRet(_pDev->Read(_recvBuffer), DeviceError::RecvErr);
        data.Append(_recvBuffer);
        return _logRetValue(true);
    }
    /**
     * @brief 输入密码(不带*号)
     * @date 2017-12-12 15:54
     * 
     * @param [in] mkIndex 主密钥序号
     * @param [in] wkIndex 工作密钥序号
     * @param [in] minLen 期望输入的最小长度
     * @param [in] maxLen 期望输入的最大长度
     * @param [in] isEnter 是否需要按回车键结束
     * @param [in] isReVoice 是否语音提示再次输入
     * @param [in] isPinblock 是否启用PINBLOCK模式
     * @param [in] pinblockByte 使用PINBLOCK模式时的后补字节
     * @param [in] accno_N 帐号
     *
     * @param [out] pwd 获取到的密文密码
     */
    bool InputPwd(byte mkIndex, byte wkIndex, byte minLen, byte maxLen, bool isEnter, 
        bool isReVoice, bool isPinblock, byte pinblockByte, const ByteArray& accno_N,
        ByteBuilder& pwd)
    {
        LOG_FUNC_NAME();
        LOGGER(_log << "主密钥索引:<" << _hex_num(mkIndex) << ">\n";
        _log << "工作密钥索引:<" << _hex_num(wkIndex) << ">\n";
        _log << "最小长度:<" << static_cast<int>(minLen) << ">,最大长度:<" << static_cast<int>(maxLen) << ">\n";
        _log << "是否回车确认:<" << isEnter << ">\n";
        _log << "是否语音再次输入:<" << isReVoice << ">\n";
        _log << "是否启用PINBLOCK模式:<" << isPinblock << ">\n";
        _log << "PINBLOCK后补字节:<" << _hex_num(pinblockByte) << ">\n";
        _log << "帐号信息:<" << accno_N.GetString() << ">\n");
        ASSERT_Device();
        _sendBuffer.Clear();
        _recvBuffer.Clear();
        DevCommand::FromAscii("30 85", _sendBuffer);
        _sendBuffer += mkIndex;
        _sendBuffer += wkIndex;
        _sendBuffer += minLen;
        _sendBuffer += maxLen;
        _sendBuffer += (isEnter ? 0x01 : 0x00);
        _sendBuffer += (isReVoice ? 0x02 : 0x01);
        _sendBuffer += (isPinblock ? 0x01 : 0x00);
        _sendBuffer += pinblockByte;
        _sendBuffer += _itobyte(accno_N.GetLength());
        _sendBuffer += accno_N;
        ASSERT_FuncErrRet(_pDev->Write(_sendBuffer), DeviceError::SendErr);
        ASSERT_FuncErrRet(_pDev->Read(_recvBuffer), DeviceError::RecvErr);
        ASSERT_FuncErrRet(_recvBuffer.GetLength() > 0, DeviceError::RecvFormatErr);
        pwd += _recvBuffer;
        LOGGER(_log << "密码密文:<";_log.WriteStream(_recvBuffer) << ">\n");
        return _logRetValue(true);
    }
    /**
     * @brief 清除密钥
     * @date 2017-12-12 15:34
     * 
     * @param [in] kType 密钥类型
     * - 类型:
     *  - 1 : 非对称密钥
     *  - 2 : 会话密钥
     *  - 3 : 主密钥
     *  - 4 : 工作密钥
     * .
     * @param [in] kIndex 密钥序号(非对称密钥时为密钥类型)
     * - 非对称密钥类型
     *  - RSA_Public  1
     *  - RSA_Private 2
     *  - SM2_Public  3
     *  - SM2_Private 4
     *  - SessionKey  5
     *  - MasterKey   6
     * .
     * @param [in] wkIndex 工作密钥序号(非对称密钥时为密钥索引)
     */
    bool ClearKey(byte kType, byte kIndex, byte wkIndex)
    {
        LOG_FUNC_NAME();
        LOGGER(_log << "密钥类型:<" << static_cast<int>(kType) << ">\n";
        _log << "密钥区号:<" << static_cast<int>(kIndex) << ">\n";
        _log << "工作密钥号:<" << static_cast<int>(wkIndex) << ">\n");
        ASSERT_Device();
        _sendBuffer.Clear();
        _recvBuffer.Clear();
        DevCommand::FromAscii("30 88", _sendBuffer);
        _sendBuffer += kType;
        _sendBuffer += kIndex;
        _sendBuffer += wkIndex;
        ASSERT_FuncErrRet(_pDev->Write(_sendBuffer), DeviceError::SendErr);
        ASSERT_FuncErrRet(_pDev->Read(_recvBuffer), DeviceError::RecvErr);
        return _logRetValue(true);
    }
    /**
    * @brief 开始带*号返回输入密码
    * @date 2017-12-13 15:50
    *
    * @param [in] minLen 期望输入的最小长度
    * @param [in] maxLen 期望输入的最大长度
    * @param [in] isEnter 是否需要按回车键结束
    * @param [in] isRevoice 是否语音提示再次输入
    */
    bool WaitPinblock(byte minLen, byte maxLen, bool isEnter, bool isReVoice)
    {
        LOG_FUNC_NAME();
        LOGGER(_log << "最小长度:<" << static_cast<int>(minLen) << ">,最大长度:<" << static_cast<int>(maxLen) << ">\n";
        _log << "是否回车确认:<" << isEnter << ">\n";
        _log << "是否语音再次输入:<" << isReVoice << ">\n");
        ASSERT_Device();
        _sendBuffer.Clear();
        _recvBuffer.Clear();
        DevCommand::FromAscii("30 86", _sendBuffer);
        _sendBuffer += minLen;
        _sendBuffer += maxLen;
        _sendBuffer += (isEnter ? 0x01 : 0x00);
        _sendBuffer += (isReVoice ? 0x02 : 0x01);
        ASSERT_FuncErrRet(_pDev->Write(_sendBuffer), DeviceError::SendErr);
        ASSERT_FuncErrRet(_pDev->Read(_recvBuffer), DeviceError::RecvErr);
        return _logRetValue(true);
    }
    /**
      * @brief 获取密码数据(带*号)
      * @date 2017-12-13 15:50
      *
      * @param [in] mkIndex 对称密钥区号30-3F
      * @param [in] wkIndex 工作密钥索引30-3F
      * @param [in] isPinblock 是否启用PINBLOCK模式
      * @param [in] pinblockByte 启用PINBLOCK时的后补字节
      * @param [in] accno_N 帐号(为空时不启用PINBLOCK模式)
      *
      * @retval Pwd : hex 输入的密码密文
      */
    bool GetPinblock(byte mkIndex, byte wkIndex, bool isPinblock, 
        byte pinblockByte, const ByteArray& accno_N, ByteBuilder& pwd)
    {
        LOG_FUNC_NAME();
        LOGGER(_log << "主密钥索引:<" << _hex_num(mkIndex) << ">\n";
        _log << "工作密钥索引:<" << _hex_num(wkIndex) << ">\n";
        _log << "是否启用PINBLOCK模式:<" << isPinblock << ">\n";
        _log << "PINBLOCK后补字节:<" << _hex_num(pinblockByte) << ">\n";
        _log << "帐号信息:<" << accno_N.GetString() << ">\n");
        ASSERT_Device();
        _sendBuffer.Clear();
        _recvBuffer.Clear();
        DevCommand::FromAscii("30 87", _sendBuffer);
        _sendBuffer += mkIndex;
        _sendBuffer += wkIndex;
        _sendBuffer += (isPinblock ? 0x01 : 0x00);
        _sendBuffer += pinblockByte;
        _sendBuffer += _itobyte(accno_N.GetLength());
        _sendBuffer += accno_N;
        ASSERT_FuncErrRet(_pDev->Write(_sendBuffer), DeviceError::SendErr);
        ASSERT_FuncErrRet(_pDev->Read(_recvBuffer), DeviceError::RecvErr);
        ASSERT_FuncErrRet(_recvBuffer.GetLength() > 0, DeviceError::RecvFormatErr);
        pwd += _recvBuffer;
        LOGGER(_log << "PINBLOCK:<";_log.WriteStream(_recvBuffer) << ">\n");
        return _logRetValue(true);
    }
    //----------------------------------------------------- 
};
//--------------------------------------------------------- 
} // namespace pinpad
} // namespace application 
} // namespace zhou_yb 
//--------------------------------------------------------- 
#endif // _LIBZHOUYB_H002PINPADEXDEVADAPTER_H_
//========================================================= 